package com.mrx.spring.security.filter;

import com.mrx.commons.model.dto.Result;
import com.mrx.spring.security.advice.SecurityAdvice;
import com.mrx.spring.security.exception.SecurityException;
import com.mrx.spring.security.service.ISecurityService;
import com.mrx.spring.security.utils.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * token 校验过滤器
 *
 * @author Mr.X
 * @since 2023-10-16 14:57
 **/
@Order(0)
@Component
public class TokenFilter extends OncePerRequestFilter {

    @Resource
    private ISecurityService securityService;

    @Resource
    private SecurityAdvice securityAdvice;

    private static final AntPathMatcher antPathMatcher = new AntPathMatcher();

    private static final Logger logger = LoggerFactory.getLogger(TokenFilter.class);

    @PostConstruct
    public void init() {
        logger.info("Using {} to Secure Api", getClass());
    }

    @Override
    protected void doFilterInternal(
            HttpServletRequest request, @NonNull HttpServletResponse response, FilterChain filterChain
    ) throws ServletException, IOException {
        try {
            String remoteAddr = request.getRemoteAddr();
            String token = request.getHeader(securityService.tokenHeader());
            logger.debug("preFilter: {}", remoteAddr);
            checkToken(token, request.getRequestURI());
            filterChain.doFilter(request, response);
        } catch (SecurityException securityException) {
            Result<?> result = securityAdvice.securityExceptionHandler(securityException);
            response.setStatus(200);
            response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
            try (ServletOutputStream outputStream = response.getOutputStream()) {
                outputStream.write(result.toJSONString().getBytes());
            }
        } finally {
            logger.debug("remove userInfo");
            SecurityUtils.clearCurrentUser();
        }
    }

    private void checkToken(String token, String url) {
        logger.debug("TokenFilter: {} -> {}", url, token);
        boolean needSecure = securityService.permitUrls().stream().noneMatch(it -> antPathMatcher.match(it, url));
        if (needSecure) {
            SecurityUtils.setCurrentUser(securityService.loadUserByToken(token));
        }
    }

}
